package com.mtech.modbus_rtu.data.base;

import com.mtech.modbus_rtu.util.UCRC16;

/**
 * 报文格式<br>
 * 
 * 
 * @author lixiaolong
 * @CreateDate 2018年3月19日
 */
public abstract class ModbusDataGram {
	protected byte addr;//8bit
	protected byte funCode;//8bit
	protected byte[] datas;//8*Nbit
	protected byte[] check;//crc16 16bit

	@SuppressWarnings("unused")
	private ModbusDataGram() {

	}

	/**
	 * 将buf解码成对象
	 * 
	 * @param buf
	 */
	public ModbusDataGram(byte[] buf) {
		this.addr = buf[0];
		this.funCode = buf[1];
		byte[] data = new byte[buf.length - 2 - 2];
		System.arraycopy(buf, 2, data, 0, data.length);
		this.datas = data;
		byte[] check = new byte[2];
		check[0] = buf[buf.length - 2];
		check[1] = buf[buf.length - 1];
		this.check = check;

		//获取除校验区之外的数据帧
		byte[] allData = new byte[buf.length - 2];
		System.arraycopy(buf, 0, allData, 0, buf.length - 2);
		//进行crc16校验
		byte[] crc = UCRC16.CRC_16(allData, allData.length);
		swapByte(crc);
		for (int i = 0; i < this.check.length; i++) {
			if (this.check[i] != crc[i]) {
				throw new RuntimeException("CRC16校验不通过");
			}
		}
	}

	/**
	 * 构造方法,数据区和校验区由子类初始化
	 * 
	 * @param addr
	 * @param funCode
	 */
	public ModbusDataGram(byte addr, byte funCode) {
		this.addr = addr;
		this.funCode = funCode;
	}

	/**
	 * 将对象中的属性拼装成数据区<br/>
	 * 不同的报文只有数据区格式不一致
	 * 
	 * @Author lixiaolong
	 * @CreateDate
	 * @return
	 */
	protected abstract byte[] madeDatas();

	/**
	 * 获取所有的数据帧数据,除了校验码.用于计算校验码
	 * 
	 * @Author lixiaolong
	 * @CreateDate
	 * @return
	 */
	private byte[] allBytesWithNoCheck() {
		byte[] result = new byte[1 + 1 + getDatas().length];
		result[0] = addr;
		result[1] = funCode;
		System.arraycopy(getDatas(), 0, result, 2, getDatas().length);
		return result;
	}

	/**
	 * 转换成对应的报文
	 * 
	 * @Author lixiaolong
	 * @CreateDate
	 * @return
	 */
	public byte[] toBytes() {
		byte[] result = new byte[1 + 1 + getDatas().length + 2];
		result[0] = addr;
		result[1] = funCode;
		System.arraycopy(getDatas(), 0, result, 2, getDatas().length);
		result[1 + getDatas().length + 1] = check[0];
		result[1 + getDatas().length + 2] = check[1];
		return result;
	}

	/**
	 * 对数据区进行CRC16计算
	 * 
	 * @Author lixiaolong
	 * @CreateDate
	 * @return
	 */
	protected byte[] checkDataBytes() {
		byte[] buf = allBytesWithNoCheck();
		byte check[] = UCRC16.CRC_16(buf, buf.length);
		swapByte(check);//check数据区的CRC16是按[低,高]位字节存放
		this.check = check;
		return check;
	}

	/**
	 * 高低字节倒转
	 * 
	 * @Author lixiaolong
	 * @CreateDate
	 * @param check
	 */
	protected void swapByte(byte[] check) {
		byte temp = check[0];
		check[0] = check[1];
		check[1] = temp;
	}

	public static ModbusFunction valueOf(String funCode) {
		int fun = Integer.valueOf(funCode);
		if (0x03 == fun) {
			return ModbusFunction.MULTI_READ;
		}
		if (0x06 == fun) {
			return ModbusFunction.ONE_WRITE;
		}
		throw new RuntimeException("不支持该方法[" + fun + "]");
	}

	//////////////getter&setter

	public byte getAddr() {
		return addr;
	}

	public void setAddr(byte addr) {
		this.addr = addr;
	}

	public byte getFunCode() {
		return funCode;
	}

	public void setFunCode(byte funCode) {
		this.funCode = funCode;
	}

	public byte[] getDatas() {
		return datas;
	}

	public void setDatas(byte[] datas) {
		this.datas = datas;
	}

	public byte[] getCheck() {
		return check;
	}

	public void setCheck(byte[] check) {
		this.check = check;
	}
	/////////////

}
